package bme.andraskindler.treenventory.imagemanipulation;

import org.opencv.android.Utils;
import org.opencv.core.Mat;
import org.opencv.imgproc.Imgproc;

import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Matrix;
import android.os.Build;
import bme.andraskindler.treenventory.TreenventoryApplication;
import bme.andraskindler.treenventory.activity.OptionsActivity;
import bme.andraskindler.treenventory.model.MeasureResult;
import bme.andraskindler.treenventory.provider.TreenventoryDataSource;

/**
 * Ez az osztály statikus függvényeken keresztül méri meg a fatörzs szélességét.
 * @author andraskindler
 *
 */
public class Filters {

	private static boolean dataReceived;
	private static Vector b1End;
	private static Vector b2End;
	private static Vector b1Start;
	private static Vector b2Start;

	public static final int CAMERA_MAIN = 0;
	public static final int CAMERA_OTHER = 1;

	/**
	 * Ez a függvény határozza meg a fatörzs szélességét.
	 * Először elforgatja a képet, ha szükséges, majd létrehozza a korrigált bitmapet, amennyiben a képkorrekció be van kapcsolva.
	 * Ezután a mérési sorokon végighaladva elvégzi az YUV(Y=0) konverziót, majd ezeken a sorokon összeszámolja a középről induló egybefüggő (azaz bizonyos határértéknél kisebb eltéréssel rendelkező) pontokat, a végén átlagol.
	 * Végül előállítja az egyeneseket a pontokból (a {@link Ray} és {@link Vector} osztályok segítségével), megvárja, míg a másodlagos telefon válaszol, majd kiszámítja a tényleges szélességet.
	 * 
	 * @param in - a fénykép
	 * @param measureheight - a mérési magasság, százalékban kifejezve
	 * @param treshold - a hibahatár
	 * @param measureCount - hány sort vegyen figyelembe az agoritmus
	 * @param cameraID - melyik kamerával készült a kép
	 * @return
	 */
	public static MeasureResult measureTrunk(final Bitmap in, final float measureheight, final float treshold, int measureCount, int cameraID) {

		dataReceived = false;

		Bitmap bitmap;
		if (Build.MODEL.equalsIgnoreCase("GT-I8160")) {
			System.out.println("GT-I8160");
			Matrix matrix = new Matrix();
			matrix.postRotate(90);
			bitmap = Bitmap.createBitmap(in, 0, 0, in.getWidth(), in.getHeight(), matrix, true);
		} else
			bitmap = in;

		Mat img = new Mat();
		Utils.bitmapToMat(bitmap, img);

		Mat dst = new Mat(img.size(), img.type());
		Imgproc.undistort(img, dst, TreenventoryDataSource.getCameraParams().getCamera(), TreenventoryDataSource.getCameraParams().getDistCoeffs());

		Bitmap correctedBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);

		final SharedPreferences preferences = TreenventoryApplication.getInstance().getSharedPreferences(OptionsActivity.SETTINGS, Context.MODE_PRIVATE);
		if (preferences.getBoolean(OptionsActivity.SETTINGS_CORRECTION, true))
			Utils.matToBitmap(dst, correctedBitmap);
		else
			Utils.matToBitmap(img, correctedBitmap);

		int sizeSum = 0;
		int leftCoordsSum = 0;
		for (int k = 0; k < measureCount; k++) {

			final int measurerow = (int) (((100 - measureheight) / 100) * correctedBitmap.getHeight() - (int) (measureCount / 2)) + k;

			float[] U = new float[correctedBitmap.getWidth()];
			float[] V = new float[correctedBitmap.getWidth()];

			for (int i = 0; i < correctedBitmap.getWidth(); i++)
				for (int j = 0; j < correctedBitmap.getHeight(); j++) {
					final int[] color = { Color.red(correctedBitmap.getPixel(i, j)), Color.green(correctedBitmap.getPixel(i, j)), Color.blue(correctedBitmap.getPixel(i, j)) };
					correctedBitmap.setPixel(i, j, correctedBitmap.getPixel(i, j));
					if (j == measurerow) {
						final float[] yuv = new float[3];
						yuv[0] = 0;
						yuv[1] = (float) (-0.147 * color[0] - 0.289 * color[1] + 0.437 * color[2] + 0.5f);
						yuv[2] = (float) (0.615 * color[0] - 0.515 * color[1] - 0.1 * color[2] + 0.5f);
						U[i] = yuv[1];
						V[i] = yuv[2];
						correctedBitmap.setPixel(i, j, Color.argb(0, 0, (int) yuv[1], (int) yuv[2]));
					}

				}

			boolean left = true;
			boolean right = true;

			final int center = (int) correctedBitmap.getWidth() / 2;
			int currentLeft = (int) correctedBitmap.getWidth() / 2;
			int currentRight = (int) correctedBitmap.getWidth() / 2;

			int size = 1;

			correctedBitmap.setPixel(center, measurerow, Color.RED);
			while (left || right) {
				if (right) {
					if (Math.abs(1 - Math.abs(U[currentRight + 1] / U[currentRight])) < 1 + treshold && Math.abs(1 - Math.abs(V[currentRight + 1] / V[currentRight])) < 1 + treshold) {
						size++;
						currentRight++;
						correctedBitmap.setPixel(currentRight, measurerow, Color.RED);
						if (currentRight + 1 == correctedBitmap.getWidth())
							right = false;
					} else
						right = false;
				}

				if (left) {
					if (Math.abs(1 - Math.abs(U[currentLeft + 1] / U[currentLeft])) < 1 + treshold && Math.abs(1 - Math.abs(V[currentLeft + 1] / V[currentLeft])) < 1 + treshold) {
						size++;
						currentLeft--;
						correctedBitmap.setPixel(currentLeft, measurerow, Color.RED);
						if (currentLeft - 1 == 0) {
							left = false;
							leftCoordsSum += currentLeft;
						}
					} else {
						left = false;
						leftCoordsSum += currentLeft;
					}
				}
			}

			sizeSum += size;

		}

		sizeSum /= measureCount;
		leftCoordsSum /= measureCount;

		final double y = (measureheight / 100) * correctedBitmap.getHeight() + (int) measureCount / 2;
		final Vector a1end = Vector.imagepointToCam(TreenventoryDataSource.getCameraParams().getCamera(), leftCoordsSum, y);
		final Vector a2end = Vector.imagepointToCam(TreenventoryDataSource.getCameraParams().getCamera(), leftCoordsSum + sizeSum, y);

		final Vector a1start = new Vector(leftCoordsSum, y, 0);
		final Vector a2start = new Vector(leftCoordsSum + sizeSum, y, 0);

		if (cameraID == CAMERA_OTHER) {
			return new MeasureResult(a1end.X, a1end.Y, a1end.Z, leftCoordsSum, y, a2end.X, a2end.Y, a2end.Z, leftCoordsSum + sizeSum, y);
		} else {

			while (!dataReceived) {
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}

			Vector intersectionLeft = Vector.closestPoint(a1end, a1start, b1End, b1Start);
			System.out.println(intersectionLeft.toString());
			Vector intersectionRight = Vector.closestPoint(a2end, a2start, b2End, b2Start);
			System.out.println(intersectionRight.toString());
			final double width = Vector.calculateDistance(intersectionLeft, intersectionRight);
			System.out.println(width);

			final double dist = Vector.calculateCamTreeDistance(new Ray(a1start, a1end), width);

			return new MeasureResult(width, correctedBitmap, dist);
		}
	}

	/**
	 * A másodlagos telefon által készített képekből kiszűrt adatok beállítására szolgál.
	 * 
	 * @param other1start a bal oldali vektor kezdőpontja
	 * @param other2start a jobb oldali vektor kezdőpontja
	 * @param other1end a bal oldali vektor végpontja
	 * @param other2end a jobb oldali vektor végpontja
	 */
	public static void setData(Vector other1start, Vector other2start, Vector other1end, Vector other2end) {
		b1End = other1end;
		b2End = other2end;
		b1Start = other1start;
		b2Start = other2start;
		dataReceived = true;
	}

}